﻿using StreamersBestFriend.Properties;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;
using Vlc.DotNet.Core;
using Vlc.DotNet.Core.Medias;
using Vlc.DotNet.Wpf;
using System.Diagnostics;
using System.ComponentModel;
using System.Collections;

namespace StreamersBestFriend
{
	class VlcMediaPlayer
	{
		public VlcControl _vlcplayer { get; internal set; }
		public exIrcClient _ircclient;
		private BotSettings _settings;
		private MainWindow _ui;
		private List<LocationMedia> prefetchedPlaylist = new List<LocationMedia>();
		private Queue<LocationMedia> LocationMediaPlayList = new Queue<LocationMedia>();
		private Object PlaylistModifiedLock = new Object();
        private System.Timers.Timer _DurationTimer;
		internal int TracksInPlaylist
		{
			get
			{
                //checks the whle playlist; debug element
				if (_vlcplayer.Medias.Count > 0)
				{
					for (int i = 0; i < _vlcplayer.Medias.Count; i++)
					{
						Debug.WriteLine(_vlcplayer.Medias.ElementAt(i).Metadatas.Title);
					}
					Debug.WriteLine("------------------------------------------");
				}

                //checks the playlist for unplayed titles, returns count
                int ia = 0;
                foreach (MediaBase m in _vlcplayer.Medias)
                {
                    if (!checkBlacklist(m.Metadatas.URL)) ia++;
                }
                return ia;
			}
		}

        /// <summary>
        /// constructor
        /// </summary>
        /// <param name="pSettings">the bot settings</param>
        /// <param name="pUi">the ui element</param>
		public VlcMediaPlayer(BotSettings pSettings, MainWindow pUi)
		{
			this._ui = pUi;
			this._settings = pSettings;
			this.initializeVLC();
			this._ircclient = new exIrcClient(pSettings, pUi, this);
		}

		// Destuctor
		~VlcMediaPlayer()
		{
            
		}

        /// <summary>
        /// initializes the vlc player with parameters, initializes events
        /// </summary>
		private void initializeVLC()
		{
			//init
			VlcContext.LibVlcDllsPath = _settings.VLC_LibVlcDllsPath;
			VlcContext.LibVlcPluginsPath = _settings.VLC_LibVlcPluginsPath;

			///settings
			// Ignore the VLC configuration file
			//VlcContext.StartupOptions.IgnoreConfig = true;
			//VlcContext.StartupOptions.LogOptions.LogInFile = true;
			//VlcContext.StartupOptions.LogOptions.ShowLoggerConsole = true;
			//VlcContext.StartupOptions.LogOptions.Verbosity = VlcLogVerbosities.Debug;
			VlcContext.StartupOptions.AddOption("--aout=waveout");
			VlcContext.StartupOptions.AddOption("--waveout-audio-device=" + _settings.VLC_WaveOutDevice);
			VlcContext.StartupOptions.AddOption("--ffmpeg-hw");
			VlcContext.StartupOptions.AddOption("--no-video");

			_vlcplayer = new VlcControl();

			///events
			_vlcplayer.Playing += _vlcplayer_Playing;
			_vlcplayer.EndReached += _vlcplayer_EndReached;
			_vlcplayer.Stopped += _vlcplayer_Stopped;
			_ui.writeConsole("VLC player initialized");
		}

        /// <summary>
        /// fires when a track starts playing (also includes the buffering of vlcplayer when fetching videodata from youtube)
        /// </summary>
		private void _vlcplayer_Playing(VlcControl sender, VlcEventArgs<EventArgs> e)
		{
            try
            {
                if (_settings.DebugMode) _ui.writeConsole("Playing event fired");

                Blacklist.SongBlacklist.Add(_vlcplayer.Media.Metadatas.URL);
                _ircclient.resetVoteCounter();
                _vlcplayer_PlaylistModified_();

                //limit song duration
                if (_DurationTimer != null) _DurationTimer.Close();
                _DurationTimer = new System.Timers.Timer(360000);
                _DurationTimer.Elapsed += _DurationTimer_Elapsed;
                _DurationTimer.Start();
            }
            catch (Exception) { }
		}

        /// <summary>
        /// fires when the maximum duration specified in _vlcplayer_playing for a song is reached; automatically skips the current song
        /// </summary>
        void _DurationTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            _ui.writeConsole("Auto skipped");
            skipSong(false);
        }

        /// <summary>
        /// fires when the end of playlist is reached or user presses the stop button
        /// </summary>
		private void _vlcplayer_Stopped(VlcControl sender, VlcEventArgs<EventArgs> e)
		{
            if (_settings.DebugMode) _ui.writeConsole("Stopped event fired");
            Blacklist.SongBlacklist.Clear();
            _vlcplayer.Medias.Clear();
		}

        /// <summary>
        /// fires when the end of a track is reached
        /// </summary>
		private void _vlcplayer_EndReached(VlcControl sender, VlcEventArgs<EventArgs> e)
		{

			if (TracksInPlaylist == 0) _ircclient.sendMessageToChannel("Last song on playlist. Please add new music.");
			if (_settings.DebugMode) _ui.writeConsole("EndReached event fired");
		}

        /// <summary>
        /// fires every time the playlist gets modified; when someone adds sth to the list and when the player changes the track
        /// updates several data
        /// </summary>
		private void _vlcplayer_PlaylistModified_()
		{
			// only 1 thread can access lock at the time
			lock (PlaylistModifiedLock)
			{
                //add stuff from prefetched list
                foreach (MediaBase m in prefetchedPlaylist)
                {
                    _vlcplayer.Medias.Add(m);
                }
                prefetchedPlaylist.Clear();

				//set currentlyplaying
				if (!File.Exists("currentlyplaying.txt")) File.Create("currentlyplaying.txt");
				if (_vlcplayer.Media != null)
				{
					File.WriteAllText("currentlyplaying.txt", _vlcplayer.Media.Metadatas.Title);
					_ui.setMainWindowTitle(_vlcplayer.Media.Metadatas.Title);
				}

                //fetch songs in playlist that havnt been played yet, save them to nextSongs
                _ui.Dispatcher.Invoke(() => _ui._TextBoxPlaylist.Text = "");
                List<String> nextSongs = new List<string>();
                List<String> allSongs = new List<string>();
                foreach (MediaBase m in _vlcplayer.Medias)
                {
                    if (!checkBlacklist(m.Metadatas.URL))
                        nextSongs.Add(m.Metadatas.Title);
                    allSongs.Add(m.Metadatas.Title + " ; " + m.Metadatas.URL);
                }

                //update ui.playlist with nextSongs
                foreach(String s in nextSongs)
                    _ui.Dispatcher.Invoke(() => _ui._TextBoxPlaylist.Text += s + "\n");

                //write nextSongs into textfile for external programs
                File.WriteAllLines("nextsongs.txt", nextSongs);
                File.WriteAllLines("allsongs.txt", allSongs);
			}
		}

        /// <summary>
        /// gets called when someone uses !songrequest [youtube url]; checks the inputstring for youtube urls and adds them to playlist if found 
        /// </summary>
        /// <param name="inputString"></param>
		public void requestSong(String inputString)
		{
			string paturl = @"(?:http|https|)(?::\/\/|)(?:www.|)(?:youtu\.be\/|youtube\.com(?:\/embed\/|\/v\/|\/watch\?v=|\|\/feeds\/api\/videos\/|\/user\S*[^\w\-\s]|\S*[^\w\-\s]))([\w\-]{11})[a-z0-9;:@?&%=+\/\$_.-]*";
			Match m = Regex.Match(inputString, paturl);
			while (m.Success)
			{
				Debug.WriteLine("Valid request:" + m.Value);
				addSongToPlaylist(m.Value);
				m = m.NextMatch();
			}
		}

        /// <summary>
        /// skips the current song
        /// </summary>
        /// <param name="chatinteraction">true if function was called by irc.chat events</param>
		public void skipSong(bool chatinteraction)
		{
			///todo: interact with tokenbot
			if (_vlcplayer.Medias.Count > 0)
			{
				if (chatinteraction) _ircclient.sendMessageToChannel("Song skipped");
				_ui.ButtonNextClick(null, null);
			}

		}

        /// <summary>
        /// adds a song to the playlist, fetches metadata from the youtubevideo object and parses them to the media object
        /// </summary>
        /// <param name="inputString">the input youtube url</param>
        private void addSongToPlaylist(String inputString)
		{
			// if song is not listed @ Blacklist and not listed @ Playlist aswell - add it
			if (!checkPlaylist(inputString) & !checkBlacklist(inputString) & !checkPrefetchedPlaylist(inputString))
			{
				Debug.WriteLine("Retrieving MetaData...");
				YoutubeVideo yt = new YoutubeVideo(inputString);
				Debug.WriteLine("checking for restrictions");

                if (yt.restricted == true)
                {
                    _ircclient.sendMessageToChannel(yt.title + " is restricted");
                    return;
                }

                if (yt.restricted == false)
                {
                    //_vlcplayer.Medias.Add(yt.media);
                    prefetchedPlaylist.Add(yt.media);
                    //_ui.Dispatcher.Invoke(() => _ui._TextBoxPlaylist.Text += yt.title + "\n");

                    yt.media.Metadatas.Title = yt.title;
                    yt.media.Metadatas.URL = inputString;
                    _ircclient.sendMessageToChannel("Added: " + yt.title);
                    _vlcplayer_PlaylistModified_();
                    return;
                }
			}
		}

        /// <summary>
        /// checks the blacklist for tracks that have already been played/got banned
        /// </summary>
        /// <param name="inputString">youtube url</param>
        /// <returns>true if found, false if not</returns>
        private bool checkBlacklist(String inputString)
		{
			// using Lambda Expression to search for MediaBase
            Debug.WriteLine("Blacklist contains: " + inputString + " " + (Blacklist.SongBlacklist.FirstOrDefault(item => item.Equals(inputString)) != null));
            if (Blacklist.SongBlacklist.FirstOrDefault(item => item.Equals(inputString)) != null) return true;
			else return false;
		}

        /// <summary>
        /// checks the current playlist if track is already on it
        /// </summary>
        /// <param name="inputString">youtube url</param>
        /// <returns>true if yes, false if no</returns>
        private bool checkPlaylist(string inputString)
		{
			// using Lambda Expression to search for MediaBase
			Debug.WriteLine("Playlist contains: " + inputString + " " + (prefetchedPlaylist.FirstOrDefault(item => item.Metadatas.URL.Equals(inputString)) != null));
			for (int i = 0; i < _vlcplayer.Medias.Count; i++)
			{
				if (_vlcplayer.Medias[i].Metadatas.URL == inputString)
					return true;
			}
			return false;

		}

        /// <summary>
        /// checks the prefetched playlist if track is already on it
        /// </summary>
        /// <param name="inputString">youtube url</param>
        /// <returns>true if yes, false if no</returns>
        private bool checkPrefetchedPlaylist(string inputString)
		{
			// using Lambda Expression to search for MediaBase
			Debug.WriteLine("prefetchedPlaylist contains: " + inputString + " " + (prefetchedPlaylist.FirstOrDefault(item => item.Metadatas.URL.Equals(inputString)) != null));
			if (prefetchedPlaylist.FirstOrDefault(item => item.Metadatas.URL.Equals(inputString)) != null) return true;
			else return false;
		}
	}
}
